package com.sunyard.manage.config;

import cn.hutool.core.util.ObjectUtil;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.sunyard.constant.manage.CommonConst;
import com.sunyard.dal.entity.Operator;
import com.sunyard.dal.mapper.OperatorMapper;
import com.sunyard.dal.mapper.PopedomMapper;
import com.sunyard.entity.JWTToken;
import com.sunyard.redisUtil.RedisUtil;
import com.sunyard.utils.Response.HeaderResult;
import com.sunyard.utils.TimeUtil;
import com.sunyard.utils.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * @ Program       :  com.ljnt.blog.filter.JWTFilter
 * @ Description   :  自定义jwt过滤器，对token进行处理
 */
@Slf4j
public class JWTFilter extends BasicHttpAuthenticationFilter {

    /**
     * 判断是否允许通过
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        log.info("isAccessAllowed方法");
        try {
            if (request instanceof HttpServletRequest) {
                if ("OPTIONS".equals(((HttpServletRequest) request).getMethod().toUpperCase())) {
                    return true;
                }
            }
            return executeLogin(request, response);
        } catch (Exception e) {
            log.info("错误:" + e);
            return false;
        }
    }

    /**
     * 创建shiro token
     *
     * @param request
     * @param response
     * @return
     */
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        log.info("createToken方法");
        String jwtToken = ((HttpServletRequest) request).getHeader("token");
        log.info("创建token时，token内容：", jwtToken);
        if (jwtToken != null) {
            return new JWTToken(jwtToken);
        }
        return null;
    }

    /**
     * isAccessAllowed为false时调用，验证失败
     *
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        log.info("onAccessDenied");
        // this.sendChallenge(request, response);
        log.info("当前用户没有该操作权限");
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setStatus(200);
        httpResponse.setCharacterEncoding("UTF-8");
        httpResponse.setContentType("application/json; charset=utf-8");
        httpResponse.getWriter().print(CommonConst.getResult(CommonConst.NO_POPEDOM, "当前用户没有该操作权限"));
        return false;
    }


    /**
     * shiro验证成功调用
     *
     * @param token
     * @param subject
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        log.info("onLoginSuccess：");
        ServletContext servletContext = request.getServletContext();
        ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        PopedomMapper popedomMapper = (PopedomMapper) context.getBean("popedomMapper");
        //List<String> list =popedomMapper.getAllpopedomLink();
        String key = "popedom:all";
        List<String> list = (List<String>) RedisUtil.get(key);
        if (ObjectUtil.isNull(list)) {
            list = popedomMapper.getAllpopedomLink();
            RedisUtil.set(key, list);
        }
        String uri = ((HttpServletRequest) request).getRequestURI();
        int indexBegin = uri.indexOf("/", 2);
        log.info(uri.substring(indexBegin));
        //TODO 权限表需完善
        if (list.contains(uri.substring(indexBegin))) {
            List<String> popedomIdList = popedomMapper.getPopedomIdByuri(uri.substring(indexBegin));
            for (String popedomId: popedomIdList) {
                JdbcRealm jdbcRealm = new JdbcRealm();
                jdbcRealm.setPermissionsLookupEnabled(true);
                subject.checkPermission(popedomId);
            }
        }
        return true;
    }


    /**
     * 拦截器的前置方法，此处进行跨域处理
     *
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        log.info("preHandle方法");
        return super.preHandle(request, response);
    }

    @Override
    public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        log.info(httpServletRequest.getRequestURI());
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        log.info("请求方法：" + httpServletRequest.getMethod());
        log.info("允许请求的方法：" + RequestMethod.OPTIONS.name());
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            log.info("OPTIONS请求不做拦截操作");
            return true;
        }
        HttpServletRequest request1 = (HttpServletRequest) request;
        String token = request1.getHeader("token");
        if (token != null) {
            String opName = "login:operator:" + TokenUtil.getOpName(token);
            Long tokenTime = TokenUtil.getCurrentTime(token);
            log.info("当前登录人：{}，当前时间：     {}", opName, TimeUtil.millisToDateStr(System.currentTimeMillis()));
            log.info("当前登录人：{}，token里的时间：{}", opName, TimeUtil.millisToDateStr(tokenTime));
            if (RedisUtil.hasKey(opName)) {
                Long redisTime = Long.parseLong(String.valueOf(RedisUtil.get(opName)));
                log.info("当前登录人：{}，redis里的时间：{}", opName, TimeUtil.millisToDateStr(redisTime));
                if (!redisTime.equals(tokenTime)) {
                    log.info("账号已在其它地方登录");
                    httpServletResponse.setCharacterEncoding("UTF-8");
                    httpServletResponse.setContentType("application/json; charset=utf-8");
                    httpServletResponse.getWriter().print(CommonConst.getResult(CommonConst.OP_OCCUPIED, "账号已在其它地方登录"));
                    return false;
                }
            } else {
                log.info("redis里的时间戳已过期，需要重新登录");
                httpServletResponse.setCharacterEncoding("UTF-8");
                httpServletResponse.setContentType("application/json; charset=utf-8");
                httpServletResponse.getWriter().print(CommonConst.getResult(CommonConst.OP_OCCUPIED, "请重新登录"));
                return false;
            }
            try {
                if (TokenUtil.verify(token)) {
                    //判断Redis是否存在所对应的RefreshToken
                    if (RedisUtil.hasKey(opName)) {
                        Long currentTimeMillisRedis = (Long) RedisUtil.get(opName);
                        if (currentTimeMillisRedis.equals(tokenTime)) {
                            String opId = TokenUtil.getOpId(token);
                            String roleId = TokenUtil.getRoleId(token);
                            String orgId = TokenUtil.getOrgId(token);
                            ServletContext servletContext = request.getServletContext();
                            ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
                            OperatorMapper operatorMapper = (OperatorMapper) context.getBean("operatorMapper");
                            Operator operator = operatorMapper.getOperatorByOpId(opId);
                            if (operator.getOpStatus() == 1) {
                                httpServletResponse.setCharacterEncoding("UTF-8");
                                httpServletResponse.setContentType("application/json; charset=utf-8");
                                httpServletResponse.getWriter().print(CommonConst.getResult(CommonConst.OP_OCCUPIED, "该用户已被停用"));
                                return false;
                            }
                            if (!roleId.equals(operator.getRoleId()) || !orgId.equals(operator.getOrgId())) {
                                httpServletResponse.setCharacterEncoding("UTF-8");
                                httpServletResponse.setContentType("application/json; charset=utf-8");
                                httpServletResponse.getWriter().print(CommonConst.getResult(CommonConst.OP_OCCUPIED, "用户信息已被更改，请重新登录"));
                                return false;
                            }
                            return super.onPreHandle(request, response, mappedValue);
                        }
                    }
                }
                return false;
            } catch (Exception e) {
                if (e instanceof TokenExpiredException) {
                    log.info("TokenExpiredException token过期 准备重新颁发令牌");
                    HttpServletResponse httpResponse = (HttpServletResponse) response;
                    httpResponse.setHeader("Authorization", token);
                    httpResponse.setHeader("Access-Control-Expose-Headers", "Authorization");
                    httpResponse.setCharacterEncoding("UTF-8");
                    httpResponse.setContentType("application/json; charset=utf-8");
                    httpResponse.getWriter().print(CommonConst.getResult(CommonConst.PAST_DUE, "token已过期"));
                    HeaderResult.setExpireHeader(httpServletRequest, httpResponse, "token已过期");
                    return false;
                }
            }

        }
        return super.onPreHandle(request, response, mappedValue);
    }
}
